- Analiza danych materiałów do superkondensatorów rozpoczęła się od wczytania i oczyszczenia danych, w tym uzupełnienia brakujących wartości – dane numeryczne zastąpiono medianą, a tekstowe wartości „NA”. Wstępne podsumowanie wykazało dużą różnorodność zmiennych numerycznych oraz kategorycznych.
- Szczegółowa analiza rozkładów zmiennych numerycznych pokazała, że większość atrybutów jest silnie skośna, a w niektórych kolumnach pojawiają się obserwacje odstające, które mogą odpowiadać zarówno błędom pomiarowym, jak i wartościom o szczególnym znaczeniu naukowym.
- Analiza korelacji zmiennych wykazała m.in., że c_at_percent i o_at_percent są silnie ujemnie skorelowane (-0.84), co wskazuje na odwrotną zależność między zawartością węgla i tlenu - więcej tlenu może zwiększać pojemność pseudopojemnościową, ale zmniejszać przewodność.
- Interaktywne wykresy pozwoliły podsumować trendy: bardzo mała powierzchnia właściwa elektrody powoduje dużą rozpiętość pojemności, natomiast większa powierzchnia (>4 m²/g) utrzymuje pojemność na niskim poziomie. Z kolei większe okno potencjału wiąże się ze spadkiem maksymalnej pojemności. To może sugerować, że nadmierna powierzchnia elektrody nie sprzyja zwiększeniu pojemności – możliwe przyczyny: słaba przewodność materiału, zbyt duże pory, ograniczenia elektrolitu.
- Model Random Forest przewidujący pojemność na podstawie pozostałych zmiennych numerycznych, analizowany za pomocą DALEX, wykazał, że najważniejszą cechą determinującą pojemność jest potential_window_v, co podkreśla kluczową rolę szerokości okna potencjału elektrody przy projektowaniu materiałów do superkondensatorów.
- Wiodący wniosek: spośród wszystkich badanych parametrów to okno potencjału elektrody (potential_window_v) jest najistotniejszym czynnikiem wpływającym na pojemność, co ma kluczowe znaczenie dla optymalizacji projektów superkondensatorów.
Wykorzystane biblioteki:
library(tidyverse)
library(knitr)
library(kableExtra)
## systemfonts and textshaping have been compiled with different versions of Freetype. Because of this, textshaping will not use the font cache provided by systemfonts
library(readr)
library(janitor)
library(lubridate)
library(ggplot2)
library(plotly)
library(corrplot)
library(ggthemes)
library(DALEX)
library(randomForest)
library(reshape2)
knitr::opts_chunk$set(
echo = TRUE,
message = FALSE,
warning = FALSE,
cache = FALSE
)
set.seed(12345)
plik <- "C:/Users/jozwi/Documents/raport_superkondensatory/data/data.csv"
df <- read_csv(plik, guess_max = 10000) %>% clean_names()
glimpse(df)
## Rows: 925
## Columns: 21
## $ ref <chr> "DOI: 10.1039/c7ta03093b…
## $ limits_of_potential_window_v <chr> "0 to 0.8", "0 to 1", "0…
## $ lower_limit_of_potential_window_v <dbl> 0.0, 0.0, 0.0, 0.0, 0.0,…
## $ upper_limit_of_potential_window_v <dbl> 0.80, 1.00, 1.00, 1.00, …
## $ potential_window_v <dbl> 0.80, 1.00, 1.00, 1.00, …
## $ current_density_a_g <dbl> 1.0, 1.0, 2.0, 5.0, 10.0…
## $ capacitance_f_g <dbl> 680, 367, 338, 283, 246,…
## $ specific_surface_area_m_2_g <dbl> 186.3, 537.0, 537.0, 537…
## $ charge_transfer_resistance_rct_ohm <dbl> NA, 6.1, 6.1, 6.1, 6.1, …
## $ equivalent_series_resistance_rs_ohm <dbl> 7.70, 1.95, 1.95, 1.95, …
## $ electrode_configuration <chr> "CNF/RGO/moOxNy", "sulfu…
## $ pore_size_nm <dbl> NA, NA, NA, NA, NA, NA, …
## $ pore_volume_cm_3_g <dbl> NA, NA, NA, NA, NA, NA, …
## $ ratio_of_id_ig <dbl> 1.450, 1.280, 1.280, 1.2…
## $ n_at_percent <dbl> 2.1, 0.0, 0.0, 0.0, 0.0,…
## $ c_at_percent <dbl> NA, 85.6, 85.6, 85.6, 85…
## $ o_at_percent <dbl> NA, 9.1, 9.1, 9.1, 9.1, …
## $ electrolyte_chemical_formula <chr> "H2SO4", "KOH", "KOH", "…
## $ electrolyte_ionic_conductivity <dbl> 7, 6, 6, 6, 6, 6, NA, NA…
## $ electrolyte_concentration_m <dbl> 1.0, 6.0, 6.0, 6.0, 6.0,…
## $ cell_configuration_three_two_electrode_system <chr> "three-electrode system"…
missing_summary <- df %>%
summarise(across(everything(), ~ sum(is.na(.)))) %>%
pivot_longer(cols = everything(), names_to = "column", values_to = "missing_count") %>%
arrange(desc(missing_count))
knitr::kable(missing_summary, caption = "Braki danych w kolumnach") %>%
kableExtra::kable_styling(full_width = F)
| column | missing_count |
|---|---|
| charge_transfer_resistance_rct_ohm | 786 |
| equivalent_series_resistance_rs_ohm | 772 |
| pore_size_nm | 769 |
| pore_volume_cm_3_g | 729 |
| o_at_percent | 703 |
| c_at_percent | 699 |
| n_at_percent | 690 |
| ratio_of_id_ig | 596 |
| specific_surface_area_m_2_g | 572 |
| electrolyte_ionic_conductivity | 99 |
| electrolyte_concentration_m | 62 |
| electrolyte_chemical_formula | 22 |
| capacitance_f_g | 17 |
| current_density_a_g | 16 |
| cell_configuration_three_two_electrode_system | 14 |
| potential_window_v | 5 |
| limits_of_potential_window_v | 4 |
| lower_limit_of_potential_window_v | 4 |
| upper_limit_of_potential_window_v | 4 |
| ref | 0 |
| electrode_configuration | 0 |
df_clean <- df %>%
mutate(
across(where(is.numeric), ~ ifelse(is.na(.), median(., na.rm = TRUE), .)),
across(where(is.character), ~ ifelse(is.na(.), "NA", .))
)
cat("Łączna liczba NA po przetworzeniu:", sum(is.na(df_clean)), "\n")
## Łączna liczba NA po przetworzeniu: 0
W przypadki jeśli kolumna ma charakter numeryczny to pusta komórka jest wypełniena medianą, jeśli charakter tekstowy to jesy wypełniana “NA”. Końcowy komunikat ukazuje pozbycie się pustych danych.
numeric_cols <- df_clean %>% select(where(is.numeric))
num_summary <- data.frame(
Column = character(),
Min = numeric(),
Median = numeric(),
Mean = numeric(),
Max = numeric(),
SD = numeric(),
stringsAsFactors = FALSE
)
for(col in names(numeric_cols)){
num_summary <- rbind(num_summary, data.frame(
Column = col,
Min = min(numeric_cols[[col]], na.rm = TRUE),
Median = median(numeric_cols[[col]], na.rm = TRUE),
Mean = round(mean(numeric_cols[[col]], na.rm = TRUE), 2),
Max = max(numeric_cols[[col]], na.rm = TRUE),
SD = round(sd(numeric_cols[[col]], na.rm = TRUE), 2),
stringsAsFactors = FALSE
))
}
knitr::kable(num_summary, caption = "Podstawowe statystyki kolumn numerycznych") %>%
kableExtra::kable_styling(full_width = F)
| Column | Min | Median | Mean | Max | SD |
|---|---|---|---|---|---|
| lower_limit_of_potential_window_v | -1.100 | 0.00000 | -0.23 | 0.200 | 0.37 |
| upper_limit_of_potential_window_v | -0.200 | 0.60000 | 0.63 | 3.500 | 0.45 |
| potential_window_v | 0.400 | 0.82500 | 0.86 | 3.500 | 0.35 |
| current_density_a_g | 0.050 | 2.00000 | 5.79 | 200.000 | 13.25 |
| capacitance_f_g | 1.400 | 260.25000 | 412.65 | 3344.080 | 443.88 |
| specific_surface_area_m_2_g | 8.896 | 159.97000 | 258.23 | 2400.000 | 359.82 |
| charge_transfer_resistance_rct_ohm | 0.080 | 1.54000 | 1.77 | 24.200 | 1.86 |
| equivalent_series_resistance_rs_ohm | 0.200 | 0.58000 | 0.75 | 17.500 | 1.06 |
| pore_size_nm | 0.530 | 4.33650 | 5.06 | 44.131 | 3.68 |
| pore_volume_cm_3_g | 0.020 | 0.21705 | 0.27 | 2.350 | 0.29 |
| ratio_of_id_ig | 0.120 | 1.05000 | 1.08 | 2.900 | 0.26 |
| n_at_percent | 0.000 | 0.00000 | 0.64 | 23.820 | 2.55 |
| c_at_percent | 1.400 | 81.00000 | 77.46 | 98.100 | 15.45 |
| o_at_percent | 1.900 | 13.70000 | 15.01 | 54.280 | 7.46 |
| electrolyte_ionic_conductivity | 1.000 | 6.00000 | 5.83 | 8.000 | 1.32 |
| electrolyte_concentration_m | 0.100 | 1.00000 | 2.47 | 6.000 | 2.16 |
categorical_cols <- df_clean %>% select(where(is.character))
unique_counts <- sapply(categorical_cols, function(col) length(unique(col)))
categorical_summary <- data.frame(
Column = names(unique_counts),
Unique_Values = as.integer(unique_counts),
stringsAsFactors = FALSE
)
knitr::kable(categorical_summary, caption = "Liczba unikalnych wartości w kolumnach kategorycznych") %>%
kableExtra::kable_styling(full_width = F)
| Column | Unique_Values |
|---|---|
| ref | 198 |
| limits_of_potential_window_v | 64 |
| electrode_configuration | 353 |
| electrolyte_chemical_formula | 24 |
| cell_configuration_three_two_electrode_system | 3 |
- Kolumny numeryczne są bardzo różnorodne, rozpiętość np. kolumny
capacitance_f_gma wartości od 1.4 do 3344.08.- Kolumna
cell_configuration_three_two_electrode_systemma tylko 3 unikalne wartości – idealne do analizy porównawczej.- Kolumna
electrode_configurationma 353 unikalne wartości – bardzo różnorodne, może wymagać grupowania do wizualizacji.
numeric_cols <- df_clean %>% select(where(is.numeric))
for(col in names(numeric_cols)){
# Histogram
p_hist <- ggplot(df_clean, aes_string(x = col)) +
geom_histogram(bins = 30, fill = "#2c7fb8", color = "white") +
theme_minimal() +
labs(title = paste("Histogram rozkładu:", col),
x = col,
y = "Liczba próbek")
print(p_hist)
# Boxplot
p_box <- ggplot(df_clean, aes_string(y = col)) +
geom_boxplot(fill = "#fdae61", color = "black") +
theme_minimal() +
labs(title = paste("Wykres pudełkowy:", col),
y = col,
x = "")
print(p_box)
}
ggplot(df_clean, aes(x = cell_configuration_three_two_electrode_system,
y = capacitance_f_g,
fill = cell_configuration_three_two_electrode_system)) +
geom_boxplot() +
theme_minimal() +
labs(title = "Porównanie pojemności w zależności od konfiguracji ogniwa",
x = "Konfiguracja ogniwa (3/2 elektrody)",
y = "Pojemność (F/g)") +
scale_fill_brewer(palette = "Set2") +
theme(legend.position = "none")
- W kolumnach upper_limit_of_potential_window_v i pootencial window_v rozkład wartości jest silnie nieregularny.
- W kolumnach current_density_a_g, capacitance_f_g można zauważyć duży spadek ilości próbek wraz ze wzrostem wartości mierzonej.
- W reszcie kolumn występuje rozkład silnie skośny, większość obserwacji skupia się wokół jednej (max dwóch) wartości.
- Z wielkości zółtych pól na wykresach pudełkowych można łatwo odczytać zróżnicowanie atrybutów. Na przykład zmienność atrybutu electrolyte_concentration_m jest bardzo duża, w porównaniu z electrolyte_concentration_m.
- Z wykresów pudełkowych można także odczytać, które z atrybutów mają stosunkowo dużo wartości odstających. Jest to istotny aspekt badania superkondensatorów, ponieważ mogą świadczyć np. o najlepszych parametrach lub uszkodzeniach
- Z Wykresu “Porównanie pojemności w zależności od konfiguracji ogniwa” możemy wywnioskować, że układ 3 elektrodowy ma najwięcej wartości odstjących
numeric_cols <- df_clean %>% select(where(is.numeric))
cor_matrix <- cor(numeric_cols, use = "complete.obs", method = "pearson")
corrplot(cor_matrix, method = "color",
type = "upper",
tl.cex = 0.7,
tl.col = "black",
addCoef.col = "black",
number.cex = 0.6,
title = "Macierz korelacji zmiennych numerycznych",
mar = c(0,0,1,0))
### Obserwacje macierzy korelacji
- Korelacja atrybutów c_at_percent i o_at_percent wynosi -0.84. Wzrost zawartości węgla wiąże się ze spadkiem zawartości tlenu i odwrotnie.
- Korelacja atrybutów charge_transfer_resistance_rct_ohm i equivalent_series_resistance_rs_ohm wynosi 0.62, co oznacza, że wzrost oporu przeniesienia ładunku (Rct) wiąże się ze wzrostem oporu szeregowego (Rs).
- Korelacja atrybutów potential_window_v i capacitance_f_g wynosi -0.34. Można ją zinterpretować w ten sposób: szerszy zakres potencjału może nieco obniżać pojemność elektrody.
p <- plot_ly(df_clean,
x = ~specific_surface_area_m_2_g,
y = ~capacitance_f_g,
type = 'scatter',
mode = 'markers',
color = ~ratio_of_id_ig,
colors = colorRamp(c("blue", "red")),
size = ~pore_volume_cm_3_g,
sizes = c(5, 30),
text = ~paste("Ref.:", ref,
"<br>Electrolyte:", electrolyte_chemical_formula,
"<br>Current density:", current_density_a_g,
"<br>Pore size (nm):", pore_size_nm),
hoverinfo = "text") %>%
layout(title = "Wykres 1: zależność powierzchni właściwej i pojemności",
xaxis = list(title = "Powierzchnia właściwa (m²/g)"),
yaxis = list(title = "Pojemność (F/g)"),
legend = list(title = list(text = "Ratio ID/IG")))
p
- Na wykresie dla bardzo małej powierzchni Widzimy bardzo dużą rozpiętość pojemności. Dla większej powierzchni (>4 m²/g) pojemność utrzymuje się na niskim poziomie. To może sugerować, że nadmierna powierzchnia elektrody nie sprzyja zwiększeniu pojemności.
p2 <- plot_ly(df_clean,
x = ~potential_window_v,
y = ~capacitance_f_g,
type = 'scatter',
mode = 'markers',
color = ~ratio_of_id_ig,
colors = colorRamp(c("green", "red")),
size = ~pore_size_nm,
sizes = c(5, 30),
text = ~paste("Ref.:", ref,
"<br>Electrolyte:", electrolyte_chemical_formula,
"<br>Surface area:", specific_surface_area_m_2_g,
"<br>Current density:", current_density_a_g),
hoverinfo = "text") %>%
layout(title = "Wykres 2: zależność okna potencjału i pojemności",
xaxis = list(title = "Okno potencjału (V)"),
yaxis = list(title = "Pojemność (F/g)"),
legend = list(title = list(text = "Ratio ID/IG")))
p2
- Na wykresie widać tendencję spadkową: im większe okno potencjału, tym maksymalna pojemność elektrody spada.
p3 <- plot_ly(df_clean,
x = ~potential_window_v,
y = ~capacitance_f_g,
z = ~pore_size_nm,
type = 'scatter3d',
mode = 'markers',
color = ~ratio_of_id_ig,
colors = colorRamp(c("blue", "red")),
size = ~specific_surface_area_m_2_g,
sizes = c(5, 30),
text = ~paste("Ref.:", ref,
"<br>Electrolyte:", electrolyte_chemical_formula,
"<br>Current density:", current_density_a_g),
hoverinfo = "text") %>%
layout(title = "Wykres 3: pojemność, okno potencjału i rozmiar porów",
scene = list(
xaxis = list(title = "Okno potencjału (V)"),
yaxis = list(title = "Pojemność (F/g)"),
zaxis = list(title = "Rozmiar porów (nm)")
),
legend = list(title = list(text = "Ratio ID/IG")))
p3
Poniżej przedstawione jest trenowanie modelu Random Forest, który przewiduje pojemność elektrody na podstawie pozostałych zmiennych numerycznych materiału. Jednocześnie używa DALEX do określenia, które cechy mają największy wpływ na przewidywaną pojemność.
library(randomForest)
library(DALEX)
num_df <- df_clean %>% select(where(is.numeric))
y <- num_df$capacitance_f_g
X <- num_df %>% select(-capacitance_f_g)
set.seed(123)
rf_model <- randomForest(X, y, ntree = 500)
explainer_rf <- DALEX::explain(rf_model,
data = X,
y = y,
label = "RandomForest_Capacitance")
## Preparation of a new explainer is initiated
## -> model label : RandomForest_Capacitance
## -> data : 925 rows 15 cols
## -> data : tibble converted into a data.frame
## -> target variable : 925 values
## -> predict function : yhat.randomForest will be used ( default )
## -> predicted values : No value for predict function target column. ( default )
## -> model_info : package randomForest , ver. 4.7.1.2 , task regression ( default )
## -> predicted values : numerical, min = 20.36265 , mean = 413.7323 , max = 2837.314
## -> residual function : difference between y and yhat ( default )
## -> residuals : numerical, min = -731.6033 , mean = -1.086883 , max = 1256.267
## A new explainer has been created!
vip <- DALEX::model_parts(explainer_rf)
plot(vip) + ggtitle("Wpływ zmiennych na predykcję pojemności (Random Forest)")
pred <- predict(rf_model, X[1:5,])
pred
## 1 2 3 4 5
## 496.7101 315.2344 306.2953 290.9368 284.5305
- Analiza XAI pokazała, że zmienna potential_window_v ma największy wpływ na przewidywaną pojemność materiałów do superkondensatorów. Oznacza to, że szerokość okna potencjału elektrody jest kluczowym parametrem determinującym pojemność.